package com.cvte.boe.media.utils;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.StatFs;

import com.cvte.boe.media.R;
import com.cvte.boe.media.model.LocalDeviceInfo;
import com.cvte.boe.media.utils.constant.DeviceDataConst;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

public class DeviceDataUtils {
    private static final int INVALID = -0x1;
    private static final String TAG = "DeviceDataUtils";
    private static HashMap<String, Integer> mStDeviceMountPath = new HashMap();
    private static HashMap<String, Integer> mFrtDeviceMountPath = new HashMap();
    private static HashMap<String, Integer> mPriorityDeviceMountPath = new HashMap();

    private static final String STORAGE_INNER = "/storage/emulated/0";
    private static final String STORAGE_EXTERNAL = "/mnt/usb/";

    static {
        mFrtDeviceMountPath.put("/mnt/sdcard/", Integer.valueOf(-0xc));
        mFrtDeviceMountPath.put("/storage/sdcard/", Integer.valueOf(-0xd));
        mFrtDeviceMountPath.put("/storage/emulated/", Integer.valueOf(-0xd));
        mPriorityDeviceMountPath.put("/storage/sd[\\w\\W]+\\d+/", Integer.valueOf(-0xb));
        mPriorityDeviceMountPath.put("/storage/external_storage/sd[\\w\\W]+\\d+/", Integer.valueOf(-0xb));
    }

    static {
        mStDeviceMountPath.put("/mnt/mmcblk", Integer.valueOf(-0xc));
        mStDeviceMountPath.put("/mnt/sd", Integer.valueOf(-0xb));
        mStDeviceMountPath.put("/mnt/usb", Integer.valueOf(-0xb));
        mStDeviceMountPath.put("/storage/udisk", Integer.valueOf(-0xb));
        mStDeviceMountPath.put("/mnt/extSdCard", Integer.valueOf(-0xc));
        mStDeviceMountPath.put("/mnt/ext_sdcard", Integer.valueOf(-0xc));
        mStDeviceMountPath.put("/storage/emulated", Integer.valueOf(-0xd));
        mStDeviceMountPath.put("/data/", Integer.valueOf(-0xd));
        mStDeviceMountPath.put("/cache/", Integer.valueOf(-0xd));
        mStDeviceMountPath.put("/storage/cifs", Integer.valueOf(-0xe));
    }

    public DeviceDataUtils() {
    }

    public static LocalDeviceInfo getDeviceFromCmdDetail(String mountPath) {
        LocalDeviceInfo deviceDto = null;
        if (mountPath == null) {
            return null;
        }
        if ((!mountPath.equals("/mnt/extSdCard")) && (!mountPath.contains("/storage/cifs"))) {
            mountPath = mountPath.toLowerCase(Locale.getDefault());
        }
        int deviceType = getDeviceTypeByPath(mountPath);
        if (INVALID != deviceType) {
            deviceDto = new LocalDeviceInfo();
            deviceDto.setDeviceType(deviceType);
            deviceDto.setMountPath(mountPath);
            deviceDto.setPhysicId(getPhysicDevId(mountPath));
            if (mountPath.startsWith("/data")) {
                deviceDto.setIsPhysicDev(0);
                deviceDto.setPhysicId("");
            } else {
                deviceDto.setIsPhysicDev(1);
                deviceDto.setPhysicId(getPhysicDevId(mountPath));
            }
            return deviceDto;
        }
        return null;
    }

    public static String getPhysicDevId(String mountPath) {
        String[] pathSplit = mountPath.split("/");
        if (pathSplit == null || (pathSplit.length < 0x4 && INVALID == getDeviceTypeByPath(mountPath))) {
            return null;
        }
        if ((mountPath.contains("/storage/cifs")) && (pathSplit.length >= 0x5)) {
            return pathSplit[0x4];
        }
        if (pathSplit.length >= 0x4) {
            return pathSplit[0x3];
        }
        if (pathSplit.length >= 0x3) {
            return pathSplit[0x2];
        }
        LogUtil.d(TAG, "################### getPhysicDevId return null ");
        return null;
    }

    public static String replaceTableName(String path) {
        return path.replace(' ', '_').replace('/', '_').replace('.', '_').replace('-', '_');
    }

    public static void sendBroadcastToDLNA(int type, String mountPath, Context context) {
        if (context == null) {
            return;
        }
        Intent intent = new Intent();
        Bundle bundle = new Bundle();
        bundle.putString(DeviceDataConst.EXTRA_RESERVE, mountPath);
        bundle.putInt(DeviceDataConst.EXTRA_DEVICE_ID, -0xb);
        intent.putExtras(bundle);
        switch (type) {
            case 1:
                intent.setAction(DeviceDataConst.ACTION_ON_DMS_UP);
                break;
            case 2:
                intent.setAction(DeviceDataConst.ACTION_ON_DMS_DOWN);
                break;
            case 3:
                intent.setAction(DeviceDataConst.ACTION_ON_DMS_BROWSE_RESULT);
                break;
        }
        context.sendBroadcast(intent);
        LogUtil.d(TAG, "BroadCast type = " + type + " path = " + mountPath);
    }

    public static int getDeviceTypeByPath(String mountPath) {
        if (mountPath != null && mountPath.startsWith("file://")) {
            mountPath = mountPath.substring("file://".length());
        }
        if (!mountPath.endsWith("/")) {
            mountPath = mountPath.concat("/");
        }
        if (mFrtDeviceMountPath.containsKey(mountPath))
            return mFrtDeviceMountPath.get(mountPath);
        for (Map.Entry<String, Integer> entry : mPriorityDeviceMountPath.entrySet()) {
            if (mountPath.matches(entry.getKey())) {
                LogUtil.w(TAG, String.format("getDeviceTypeByPath path:%s, matches:true type:%d", mountPath, entry.getValue()));
                return entry.getValue();
            }
        }
        Set<String> keys = mStDeviceMountPath.keySet();
        Iterator<String> iterator = keys.iterator();
        while (iterator.hasNext()) {
            String path = iterator.next();
            if (mountPath.startsWith(path)) {
                return mStDeviceMountPath.get(path).intValue();
            }
        }
        return INVALID;
    }

    public static List<String> getMountPathList() {
        List<String> filepath = new ArrayList<>();
        // add inner storage
        filepath.add(STORAGE_INNER);

        // scan the usb or ext_sdcard , they all mount on /mnt/usb
        String[] scanList = {STORAGE_EXTERNAL,};
        for (String dir : scanList) {
            File rootFile = new File(dir);
            if (rootFile != null && rootFile.exists()) {
                for (File file : rootFile.listFiles()) {
                    if (file.isDirectory() && (file.canRead() || file.canWrite())) {
                        filepath.add(file.getPath());
                    }
                }
            }
        }
        return filepath;
    }

    /*
     * chenyunzhou@20150907 add
     * get all the devices : usb , sdcard, inner storage
     */
    public static List<LocalDeviceInfo> getLocalDeviceInfoList(Context mContext) {
        List<LocalDeviceInfo> list = new ArrayList<>();
        List<String> filepath = new ArrayList<>();
        // add inner storage
        filepath.add(STORAGE_INNER);

        // scan the usb or ext_sdcard , they all mount on /mnt/usb
        String[] scanList = {STORAGE_EXTERNAL,};
        for (String dir : scanList) {
            File rootFile = new File(dir);
            if (rootFile != null && rootFile.exists()) {
                for (File file : rootFile.listFiles()) {
                    if (file.isDirectory() && (file.canRead() || file.canWrite())) {
                        filepath.add(file.getPath());
                    }
                }
            }
        }

        // construct display item and add to list
        for (String mountPath : filepath) {
            LocalDeviceInfo dto = DeviceDataUtils.getDeviceFromCmdDetail(mountPath);
            if (dto != null) {
                getLocDevSizeByStatFs(mContext, dto);
                list.add(dto);
            }
        }

        return list;
    }

    /*
     * chenyunzhou@20150907 add
     * set device info
     */
    public static void getLocDevSizeByStatFs(Context mContext, LocalDeviceInfo deviceDto) {
        String path = deviceDto.getMountPath();
        if (path == null) {
            LogUtil.e(TAG, "DeviceDataSourceCmd getLocDevSizeByStatFs getMountPath is null");
            return;
        }
        try {
            StatFs stat = new StatFs(path);
            long blockSize = (long) (stat.getBlockSize() >> 0xa);
            long totalBlocks = (long) stat.getBlockCount();
            long freeBlocks = (long) stat.getFreeBlocks();
            long usedBlocks = totalBlocks - freeBlocks;
            float usedPercent = (float) usedBlocks / totalBlocks;
            deviceDto.setTotalSize(DiskUtil.getDiskSizeStringL(mContext, (blockSize * totalBlocks), R.string.unknown, R.string.unit_disk_size_kb, R.string.unit_disk_size_mb, R.string.unit_disk_size_gb, R.string.unit_disk_size_tb));
            deviceDto.setUsedSize(DiskUtil.getDiskSizeStringL(mContext, (usedBlocks * blockSize), R.string.unknown, R.string.unit_disk_size_kb, R.string.unit_disk_size_mb, R.string.unit_disk_size_gb, R.string.unit_disk_size_tb));
            deviceDto.setFreeSize(DiskUtil.getDiskSizeStringL(mContext, (blockSize * freeBlocks), R.string.unknown, R.string.unit_disk_size_kb, R.string.unit_disk_size_mb, R.string.unit_disk_size_gb, R.string.unit_disk_size_tb));
            deviceDto.setUsedPercent(usedPercent);
            return;
        } catch (IllegalArgumentException e) {
            LogUtil.e(TAG, "DeviceDataSourceCmd getLocDevSizeByStatFs IllegalArgumentException: Invalid path:" + path);
        }
    }

    public static void getLocDevName(LocalDeviceInfo deviceDto) {
        try {
            deviceDto.setDevName(deviceDto.getMountPath());
            Class<?> serviceManager = Class.forName("android.os.ServiceManager");
            Method getService = serviceManager.getDeclaredMethod("getService", String.class);
            IBinder service = (IBinder) getService.invoke(null, "mount");
            if (service != null) {
                Class<?> iMountServiceStub = Class.forName("android.os.storage.IMountService$Stub");
                Method asInterface = iMountServiceStub.getDeclaredMethod("asInterface", IBinder.class);
                Object mountService = asInterface.invoke(null, service);

                if (mountService != null) {
                    Method getAllExtraInfos = mountService.getClass().getDeclaredMethod("getAllExtraInfos");
                    List<Object> mountList = (List<Object>) getAllExtraInfos.invoke(mountService);

                    if (mountList != null) {
                        Class<?> extraInfo = mountList.get(0).getClass();
                        Field mDiskLabel = extraInfo.getDeclaredField("mDiskLabel");
                        Field mMountPoint = extraInfo.getDeclaredField("mMountPoint");
                        for (int i = 0; i < mountList.size(); i++) {
                            String tmpPath = (String) mMountPoint.get(mountList.get(i));
                            if (tmpPath.equals(deviceDto.getMountPath())
                                    || tmpPath.concat("/").equals(deviceDto.getMountPath())) {
                                deviceDto.setDevName(EncodeUtil.getUSBname((String) mDiskLabel.get(mountList.get(i))));
                            }

                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static boolean isSupportMountPath(String mountPath) {
        if (getDeviceTypeByPath(mountPath) != INVALID) {
            LogUtil.d(TAG, "isSupportMountPath<#true>:" + mountPath);
            return true;
        }
        LogUtil.d(TAG, "isSupportMountPath<#false>:" + mountPath);
        return false;
    }

    public static String[] getStDeviceMountPath() {
        Set<String> keys = mStDeviceMountPath.keySet();
        String[] arrays = new String[mStDeviceMountPath.size()];
        keys.toArray(arrays);
        return arrays;
    }

    public static boolean isInnerStorage(String mountPath) {
        if (mFrtDeviceMountPath.containsKey(mountPath)) {
            return -0xd == mFrtDeviceMountPath.get(mountPath);
        }
        return false;
    }
}
